package org.nativejpa.query;

import org.w3c.dom.Element;
import org.nativejpa.util.XMLUtils;
import org.nativejpa.util.ClassUtils;

import javax.persistence.SqlResultSetMapping;
import javax.persistence.EntityResult;
import javax.persistence.ColumnResult;
import javax.persistence.FieldResult;
import javax.persistence.PersistenceException;
import java.util.List;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Collection;
import java.util.Collections;

/**
 * @author <a href="mailto:jfox.young@gmail.com">Young Yang</a>
 * @create 2009-5-18 21:52:29
 */
public class ResultsetMapping {

    private String name;

    private List<EntityResultMapping> entityResults = new ArrayList<EntityResultMapping>(2);
    private List<String> columnResults = new ArrayList<String>(8);

    public ResultsetMapping(SqlResultSetMapping sqlResultSetMapping) {
        this.name = sqlResultSetMapping.name();
        for(EntityResult entityResultAnnotation : sqlResultSetMapping.entities()){
            entityResults.add(new EntityResultMapping(entityResultAnnotation));
        }

        for(ColumnResult columnResultAnnotation : sqlResultSetMapping.columns()){
            columnResults.add(columnResultAnnotation.name().toUpperCase());
        }
    }

    public ResultsetMapping(Element sqlResultsetMappingElement) {
        this.name = XMLUtils.getAtrributeValue(sqlResultsetMappingElement, "name");
        List<Element> entityResultElements = XMLUtils.getChildElementsByTagName(sqlResultsetMappingElement, "entity-result");
        for(Element entityResultElement : entityResultElements){
            entityResults.add(new EntityResultMapping(entityResultElement));
        }
        List<Element> columnResultElements = XMLUtils.getChildElementsByTagName(sqlResultsetMappingElement, "column-result");
        for(Element columnResultElement : columnResultElements) {
            String name = XMLUtils.getAtrributeValue(columnResultElement, "name");
            columnResults.add(name.toUpperCase());
        }
        //TODO:
    }

    public String getName() {
        return name;
    }

    public List<EntityResultMapping> getEntityResults(){
        return Collections.unmodifiableList(entityResults);
    }

    public List<String> getColumnResults(){
        return Collections.unmodifiableList(columnResults);
    }
}

class EntityResultMapping {
    private Class entityClass;
    // the column name (or alias) of the column in the SELECT list that is used to determine the type of the entity instance.
    private String discriminatorColumn;
    private List<FieldResultMapping> fieldResults = new ArrayList<FieldResultMapping>(8);
    private Map<String, String> fieldMapping = new HashMap<String, String>(8);

    EntityResultMapping(EntityResult entityResult) {
        this.entityClass = entityResult.entityClass();
        this.discriminatorColumn = entityResult.discriminatorColumn();
        for(FieldResult fieldResultAnnotation : entityResult.fields()){
            FieldResultMapping fieldResult = new FieldResultMapping(fieldResultAnnotation.name(), fieldResultAnnotation.column());
            fieldResults.add(fieldResult);
            fieldMapping.put(fieldResult.getColumn().toUpperCase(), fieldResult.getName());
        }
    }

    EntityResultMapping(Element entityResultElement) {
        String entityClassName = XMLUtils.getAtrributeValue(entityResultElement, "entity-class");
        try {
            this.entityClass = ClassUtils.getClass(entityClassName);
        }
        catch (ClassNotFoundException e) {
            throw new PersistenceException("Can not load entity class: " + entityClassName + " defined in sql-result-set-mapping.", e);
        }
        this.discriminatorColumn = XMLUtils.getAtrributeValue(entityResultElement, "discriminator-column");
        List<Element> fieldResultElements = XMLUtils.getChildElementsByTagName(entityResultElement, "field-result");
        for(Element fieldResultElement : fieldResultElements){
            FieldResultMapping fieldResult = new FieldResultMapping(XMLUtils.getAtrributeValue(fieldResultElement, "name"), XMLUtils.getAtrributeValue(fieldResultElement, "column"));
            fieldResults.add(fieldResult);
            fieldMapping.put(fieldResult.getColumn().toUpperCase(), fieldResult.getName());
        }
    }

    public Class getEntityClass() {
        return entityClass;
    }

    public String getDiscriminatorColumn() {
        return discriminatorColumn;
    }

    public List<FieldResultMapping> getFieldResultMappings(){
        return Collections.unmodifiableList(fieldResults);
    }

    public String getFieldName(String columnName) {
        if(fieldMapping.containsKey(columnName.toUpperCase())){
            return fieldMapping.get(columnName.toUpperCase());
        }
        else {
            return columnName;
        }
    }
}

class FieldResultMapping {
    // class filed name
    private String name;
    // column name in select
    private String column;

    FieldResultMapping(String name, String column) {
        this.name = name;
        this.column = column;
    }

    public String getName() {
        return name;
    }

    public String getColumn() {
        return column;
    }
}
